#include <iostream>
#include <cstring>
#include <cstdio>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string>
#include <vector>
#include <dirent.h>
#include <stdexcept>
#include <fcntl.h>   // 用于open、O_RDONLY、O_CREAT等
#include <unistd.h>  // 可能还需要，用于close等函数

// 常量定义（替代宏）
const int kCommandSize = 1024;               // 命令行最大长度
const char* kFormatFull = "[%s@%s %s]$ ";    // 完整提示符格式
const char* kFormatShort = "[%s]$ ";         // 简化提示符格式
const int kMaxArgc = 128;                    // 最大参数个数
const int kMaxEnvs = 100;                    // 最大环境变量个数
const char* kSeparator = " ";                // 命令参数分隔符

// 重定向类型枚举（替代宏）
enum RedirType 
{
    kNoneRedir = 0,
    kInputRedir = 1,    // < 输入重定向
    kOutputRedir = 2,   // > 输出重定向
    kAppendRedir = 3    // >> 追加重定向
};

// Shell上下文类（封装全局变量，降低耦合）
class ShellContext 
{
public:
    char* cmd_argv[kMaxArgc] = {nullptr};  // 命令参数数组
    int cmd_argc = 0;                      // 命令参数个数
    std::vector<std::string> env;          // 环境变量（替代char*数组）
    RedirType redir_type = kNoneRedir;     // 当前重定向类型
    std::string redir_filename;            // 重定向目标文件名
    char cwd[1024] = {0};                  // 当前工作目录
    char cwdenv[1024] = {0};               // 格式化的PWD环境变量
    int last_exit_code = 0;                // 上一条命令退出码
};

// 全局上下文实例（单例模式简化调用）
ShellContext g_shell_ctx;

/**
 * @brief 获取当前用户名
 * @return 用户名字符串（若获取失败返回"none"并打印错误）
 */
const char* GetUserName()
{
    const char *name = getenv("USER");

    if (nullptr == name)
    {
        std::cerr << "Warning: Failed to get USER environment variable" << std::endl;
        return "none";
    }

    return name;
}

/**
 * @brief 获取当前主机名
 * @return 主机名字符串（若获取失败返回"none"并打印错误）
 */
const char* GetHostName() 
{
    const char* hostname = getenv("HOSTNAME");

    if (hostname == nullptr) 
    {
        std::cerr << "Warning: Failed to get HOSTNAME environment variable" << std::endl;
        return "none";
    }

    return hostname;
}

/**
 * @brief 获取当前工作目录并更新PWD环境变量
 * @return 当前工作目录字符串（若获取失败返回"none"并打印错误）
 */
const char* GetPwd()
{
    if (nullptr == getcwd(g_shell_ctx.cwd, sizeof(g_shell_ctx.cwd))) 
    {
        std::cerr << "Error: Failed to get current working directory (" << strerror(errno) << ")" << std::endl;
        return "none";
   }

    snprintf(g_shell_ctx.cwdenv, sizeof(g_shell_ctx.cwdenv), "PWD=%s", g_shell_ctx.cwd);
    putenv(g_shell_ctx.cwdenv);
    return g_shell_ctx.cwd;
}

/**
 * @brief 获取用户主目录
 * @return 主目录字符串（若获取失败返回空字符串并打印错误）
 */
const char* GetHome() 
{
    const char* home = getenv("HOME");
    if (home == nullptr) 
    {
        std::cerr << "Warning: Failed to get HOME environment variable" << std::endl;
        return "";
    }
    return home;
}

/**
 * @brief 处理cd命令（切换目录）
 * @return 切换成功返回true，失败返回false
 */
bool HandleCd() 
{
    std::string target_dir;
    if (g_shell_ctx.cmd_argc == 1) 
    {
        // 无参数时切换到主目录
        target_dir = GetHome();
        if (target_dir.empty()) 
        {
            std::cerr << "Error: HOME directory not set" << std::endl;
            return false;
        }
    } 
     else 
     {
        // 有参数时使用指定路径
        target_dir = g_shell_ctx.cmd_argv[1];
     }

    // 执行目录切换
    if (chdir(target_dir.c_str()) != 0) 
    {
        std::cerr << "Error: cd failed (" << strerror(errno) << ")" << std::endl;
        return false;
    }
    return true;
}

/**
 * @brief 处理echo命令（打印内容）
 */
void HandleEcho() 
{
    if (g_shell_ctx.cmd_argc < 2) 
    {
        // 无参数时打印空行
        std::cout << std::endl;
        return;
    }

    std::string arg = g_shell_ctx.cmd_argv[1];
    if (arg == "$?") 
    {
        // 打印上一条命令退出码
        std::cout << g_shell_ctx.last_exit_code << std::endl;
        g_shell_ctx.last_exit_code = 0;  // 重置退出码
    } 
    else if (arg[0] == '$' && arg.size() > 1) 
    {
        // 打印环境变量（如$USER）
        std::string env_name = arg.substr(1);
        const char* env_value = getenv(env_name.c_str());
        if (env_value) 
        {
            std::cout << env_value << std::endl;
        } 
        else 
        {
            std::cerr << "Error: Environment variable $" << env_name << " not found" << std::endl;
        }
    } 
    else 
    {
        // 直接打印参数
        std::cout << arg << std::endl;
    }
}

/**
 * @brief 初始化环境变量（继承系统环境变量并添加自定义变量）
 */
void InitEnv()
{
    extern char **environ;  // 系统环境变量全局数组
    if (nullptr == environ) return;

    // 复制系统变量
    for (int i = 0; environ[i] != nullptr; ++i)
    {
        if (g_shell_ctx.env.size() >= kMaxEnvs - 1)
        {
            // 预留最后一个位置放nullptr
             std::cerr << "Warning: Too many environment variables, truncating" << std::endl;
            break;
        }
         g_shell_ctx.env.push_back(environ[i]);
    }

    // 添加自定义环境变量
    g_shell_ctx.env.push_back("A=test");

    // 将环境变量注册到系统
    for (const auto& env_str : g_shell_ctx.env) 
    {
        if (putenv(const_cast<char*>(env_str.c_str())) != 0) 
        {
            std::cerr << "Warning: Failed to set environment variable " << env_str << std::endl;
        }
    }
}

/**
 * @brief 提取路径中的最后一级目录名
 * @param pwd 完整路径字符串
 * @return 最后一级目录名字符串
 */
std::string GetDirName(const char* pwd) 
{
    if (pwd == nullptr || *pwd == '\0') 
    {
        return "";
    }
    std::string path_str(pwd);
    // 移除末尾的 '/'（如果有）
    if (path_str.back() == '/') 
    {
        path_str.pop_back();
    }
    // 查找最后一个 '/' 的位置
    size_t last_slash = path_str.find_last_of('/');
    if (last_slash == std::string::npos) 
    {
        // 没有 '/'，返回整个路径
        return path_str;
    }
    else 
    {
        // 返回 '/' 后面的部分（文件名）
        return path_str.substr(last_slash + 1);
    }
}

/**
 * @brief 生成命令提示符字符串
 * @param prompt 存储提示符的缓冲区
 * @param size 缓冲区大小
 */
void MakeCommandPrompt(char* prompt, int size)
{
    snprintf(prompt, size, kFormatFull, 
            GetUserName(), 
            GetHostName(),
            GetDirName(GetPwd()).c_str());
}

/**
 * @brief 打印命令提示符
 */
void PrintCommandPrompt()
{
    char prompt[kCommandSize];
    MakeCommandPrompt(prompt, sizeof(prompt));
    printf("%s", prompt);
    fflush(stdout);
}

/**
 * @brief 读取用户输入的命令行
 * @param out 存储命令行的缓冲区
 * @param size 缓冲区大小
 * @return 读取成功返回true，失败（如EOF）返回false
 */
bool GetCommandLine(char* out, int size)
{
    if (nullptr == fgets(out, size, stdin)) return false;

    // 移除末尾的换行符（避免空输入时越界）
    size_t len = strlen(out);
    if (len > 0 && out[len - 1] == '\n') 
    {
        out[len - 1] = '\0';
    }
    return true;
}

/**
 * @brief 去除命令行末尾的空格
 * @param commandline 命令行字符串
 * @param end 末尾索引（引用传递，修改为非空格字符的索引）
 */
void TrimTrailingSpace(char* commandline, int& end) 
{
    while (end >= 0 && isspace(static_cast<unsigned char>(commandline[end]))) 
    {
        end--;
    }
}

/**
 * @brief 检查并处理命令行中的重定向符号
 * @param commandline 命令行字符串（会被修改为不含重定向的部分）
 */
void CheckRedirection(char* commandline) 
{
    g_shell_ctx.redir_type = kNoneRedir;
    g_shell_ctx.redir_filename.clear();

    int len = strlen(commandline);
    if (len == 0) return;

    int end = len - 1;
    TrimTrailingSpace(commandline, end);
    if (end < 0) return;  // 空命令

    // 检查输入重定向 <
    if (commandline[end] == '<') 
    {
        commandline[end] = '\0';  // 截断命令部分
        TrimTrailingSpace(commandline, end);
        g_shell_ctx.redir_type = kInputRedir;
        g_shell_ctx.redir_filename = commandline + end + 1;  // 提取文件名
    }
    // 检查输出重定向 > 或 >>
    else if (commandline[end] == '>') 
    {
        if (end > 0 && commandline[end - 1] == '>') 
        {
            // 追加重定向 >>
            commandline[end - 1] = '\0';  // 截断命令部分
            int new_end = end -1;
            TrimTrailingSpace(commandline, new_end);
            g_shell_ctx.redir_type = kAppendRedir;
            g_shell_ctx.redir_filename = commandline + end;  // 提取文件名
        } else {
            // 覆盖重定向 >
            commandline[end] = '\0';  // 截断命令部分
            TrimTrailingSpace(commandline, end);
            g_shell_ctx.redir_type = kOutputRedir;
            g_shell_ctx.redir_filename = commandline + end + 1;  // 提取文件名
        }
    }
}

/**
 * @brief 解析命令行字符串为参数数组
 * @param commandline 命令行字符串
 * @return 解析成功（有参数）返回true，失败返回false
 */
bool ParseCommand(char* commandline) 
{
    // 重置参数数组
    for (int i = 0; i < g_shell_ctx.cmd_argc; ++i) 
    {
        g_shell_ctx.cmd_argv[i] = nullptr;
    }
    g_shell_ctx.cmd_argc = 0;

    // 分割命令行参数
    char* token = strtok(commandline, kSeparator);
    while (token != nullptr && g_shell_ctx.cmd_argc < kMaxArgc - 1) {
        g_shell_ctx.cmd_argv[g_shell_ctx.cmd_argc++] = token;
        token = strtok(nullptr, kSeparator);
    }
    g_shell_ctx.cmd_argv[g_shell_ctx.cmd_argc] = nullptr;  // 数组末尾置空

    return g_shell_ctx.cmd_argc > 0;
}

/**
 * @brief 检查并执行内置命令
 * @return 执行了内置命令返回true，否则返回false
 */
bool CheckAndExecuteBuiltin() 
{
    if (g_shell_ctx.cmd_argc == 0 || g_shell_ctx.cmd_argv[0] == nullptr) 
    {
        return false;
    }

    std::string cmd = g_shell_ctx.cmd_argv[0];
    if (cmd == "cd") 
    {
        HandleCd();
        return true;
    } 
    else if (cmd == "echo") 
    {
        HandleEcho();
        return true;
    } 
    else if (cmd == "export") 
    {   
        // 预留export命令实现
        return true;
    }
    return false;
}

/**
 * @brief 执行外部命令（通过子进程）
 */
void ExecuteExternalCommand() 
{
    pid_t pid = fork();
    if (pid == -1) 
    {
        std::cerr << "Error: Failed to fork process (" << strerror(errno) << ")" << std::endl;
        return;
    }

    if (pid == 0) 
     {  
        // 子进程
        int fd = -1;
        // 处理重定向
        switch (g_shell_ctx.redir_type) 
        {
            case kInputRedir:
                fd = open(g_shell_ctx.redir_filename.c_str(), O_RDONLY);
                break;
            case kOutputRedir:
                // 权限设置为0600（仅所有者可读写，更安全）
                fd = open(g_shell_ctx.redir_filename.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0600);
                break;
            case kAppendRedir:
                fd = open(g_shell_ctx.redir_filename.c_str(), O_CREAT | O_WRONLY | O_APPEND, 0600);
                break;
            default:
                fd = -1;
        }

        // 重定向文件描述符
        if (fd != -1) 
        {
            int target_fd = (g_shell_ctx.redir_type == kInputRedir) ? STDIN_FILENO : STDOUT_FILENO;
            if (dup2(fd, target_fd) == -1) 
            {
                std::cerr << "Error: Failed to redirect fd (" << strerror(errno) << ")" << std::endl;
                exit(1);
            }
            close(fd);  // 关闭原始文件描述符
        }

        // 执行外部命令
        execvp(g_shell_ctx.cmd_argv[0], g_shell_ctx.cmd_argv);
        // 若execvp失败，打印错误并退出
        std::cerr << "Error: Command not found (" << g_shell_ctx.cmd_argv[0] << ")" << std::endl;
        exit(1);
    } 
       else 
       {  
        // 父进程
        int status;
        pid_t wait_result = waitpid(pid, &status, 0);
        if (wait_result == -1) 
        {
            std::cerr << "Error: Failed to wait for child process (" << strerror(errno) << ")" << std::endl;
            g_shell_ctx.last_exit_code = -1;
        } else {
            // 提取子进程退出码
            g_shell_ctx.last_exit_code = WEXITSTATUS(status);
        }
    }
}


int main() 
{
    try 
    {
        InitEnv();  // 初始化环境变量

        while (true) 
        {  // Shell主循环
            PrintCommandPrompt();  // 打印提示符

            char commandline[kCommandSize] = {0};
            if (!GetCommandLine(commandline, sizeof(commandline))) 
            {
                break;  // 读取失败（如Ctrl+D），退出循环
            }

            CheckRedirection(commandline);  // 处理重定向

            if (!ParseCommand(commandline)) 
            {
                continue;  // 解析命令失败，跳过
            }

            if (CheckAndExecuteBuiltin()) 
            {
                continue;  // 执行内置命令成功，继续循环
            }

            ExecuteExternalCommand();  // 执行外部命令
        }
    } catch (const std::exception& e) 
    {
        std::cerr << "Fatal error: " << e.what() << std::endl;
        return 1;
    }

    return 0;
}
